#include <d3dx9.h>
#include <d3d9.h>
#include <Windows.h>
#include <assert.h>

#include "Camera.h"
#include "VertexBuffer.h"
#include "Renderer.h"

#define COLOR_VERTEX_FVF D3DFVF_XYZ|D3DFVF_DIFFUSE

D3DPRIMITIVETYPE primitives[6] = {D3DPT_POINTLIST, D3DPT_LINELIST, D3DPT_LINESTRIP, 
									D3DPT_TRIANGLELIST, D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN};

D3DTRANSFORMSTATETYPE matrixModes[3] = {D3DTS_WORLDMATRIX(0),D3DTS_VIEW,D3DTS_PROJECTION};

MegaEngine::Renderer::Renderer(HWND window)
{
	viewerMatrix = NULL;
	graphCardHandle = NULL;
	winHandle = window;
	colVertexBuffer = NULL;
	if(winHandle == NULL)
	{
		//fuck
		assert(false);
		throw;
	}
}

bool MegaEngine::Renderer::Init()
{
	D3DPRESENT_PARAMETERS PresentParams; // D3D initialization parameters
	memset(&PresentParams, 0, sizeof(D3DPRESENT_PARAMETERS)); // set the whole memory block to 0

	IDirect3D9* directGraphicsHandle = Direct3DCreate9(D3D_SDK_VERSION);	// get a handle to DirectX's directGraphics, not keeping it due to it being present
																			// within the graphCardHandle that's created next

	D3DDISPLAYMODE displayMode;
	directGraphicsHandle->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode);
	PresentParams.BackBufferFormat = displayMode.Format;
	PresentParams.Windowed = true;
	PresentParams.BackBufferCount = 1; 
	PresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
	PresentParams.hDeviceWindow = 0; 
	PresentParams.Flags = 0;
	PresentParams.EnableAutoDepthStencil = TRUE;
	PresentParams.AutoDepthStencilFormat = D3DFMT_D16;
	PresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	PresentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	if(directGraphicsHandle == NULL)
	{
		//fuck
		assert(false);
		return false;
	}

	if(directGraphicsHandle->CreateDevice(D3DADAPTER_DEFAULT,
		D3DDEVTYPE::D3DDEVTYPE_HAL, winHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, 
		&PresentParams,&graphCardHandle) != D3D_OK) // try to obtain 
	{
		if(directGraphicsHandle->CreateDevice(D3DADAPTER_DEFAULT,
			D3DDEVTYPE::D3DDEVTYPE_HAL, winHandle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
			&PresentParams,&graphCardHandle) != D3D_OK)
		{
			//fuck
			assert(false);
			directGraphicsHandle->Release();
			graphCardHandle = NULL;
			return false;
		}
	}
	
	if(graphCardHandle == NULL)
	{
		//shit
		assert(false);
		directGraphicsHandle->Release();
		return false;
	}

	graphCardHandle->SetRenderState(D3DRS_AMBIENT,RGB(255,255,255));
	//lighting disabled
	graphCardHandle->SetRenderState(D3DRS_LIGHTING,false);
	//don't cull backside
	graphCardHandle->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
	//enable depth buffering
	graphCardHandle->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE);
	//render as wireframe
	//graphCardHandle->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

	//----------------------------------------------
	/* camera init */
	viewerPos = new D3DXVECTOR3;
	viewerUp = new D3DXVECTOR3;
	projectionMatrix = new D3DXMATRIX;
	viewerMatrix = new D3DXMATRIX;

	ZeroMemory(viewerPos, sizeof(D3DXVECTOR3));
	ZeroMemory(viewerUp, sizeof(D3DXVECTOR3));
	viewerUp->y = 1;
	viewerPos->z = 10;
	matrixMode = View;

	loadIdentity();
	setViewportPosition();

	D3DVIEWPORT9 viewport;
	if(graphCardHandle->GetViewport(&viewport) != D3D_OK)
	{
		assert(false);
		throw;
	}

	D3DXMatrixPerspectiveFovLH(projectionMatrix, D3DX_PI /2.0f, (float) 4/3, 0.5f, 2000.0f);
	if( graphCardHandle->SetTransform(D3DTS_PROJECTION, projectionMatrix) != D3D_OK)
	{
		assert(false);
		throw;
	}

	
	//loadIdentity();
	/**/
	return true;
}

MegaEngine::Renderer::~Renderer()
{
	if(graphCardHandle)
	{
		IDirect3D9* directGraphicsHandle;
		graphCardHandle->GetDirect3D(&directGraphicsHandle);
		graphCardHandle->Release();
		directGraphicsHandle->Release();
		if(colVertexBuffer) delete colVertexBuffer;
	}
	if(viewerPos)
		delete viewerPos;
	if(viewerUp)
		delete viewerUp;
	if(projectionMatrix)
		delete projectionMatrix;
	if(viewerMatrix)
		delete viewerMatrix;
}

bool MegaEngine::Renderer::BeginScene()
{
	if(graphCardHandle->BeginScene() != D3D_OK)
	{
		return false;
	}

	return graphCardHandle->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0) == D3D_OK;
}

bool MegaEngine::Renderer::EndScene()
{
	if(graphCardHandle->EndScene() != D3D_OK)
	{
		return false;
	}
	graphCardHandle->Present(NULL, NULL, NULL, NULL);
	return true;

}

bool MegaEngine::Renderer::Draw(DrawPrimitives primitive, int vertexCount, ColorVertex* vertexs)
{
	if(!colVertexBuffer)
	{
		colVertexBuffer = new VertexBuffer<ColorVertex, COLOR_VERTEX_FVF>(graphCardHandle, true);
	}
	((VertexBuffer<ColorVertex, COLOR_VERTEX_FVF>*)colVertexBuffer)->Bind();
	return ((VertexBuffer<ColorVertex, COLOR_VERTEX_FVF>*)colVertexBuffer)->Draw(vertexs, primitives[primitive], vertexCount);
}

//----------------------------------------------------
//Matrix handling

void MegaEngine::Renderer::loadIdentity()
{
	D3DXMATRIX tempMat;
	D3DXMatrixIdentity(&tempMat);
	if(matrixMode == View)
	{
		D3DXVECTOR3 eyePos(0,0,-1);
		D3DXVECTOR3 lookPos(0,0,0);
		D3DXVECTOR3 upVector(0,1,0);

		D3DXMatrixLookAtLH(&tempMat, &eyePos, &lookPos, &upVector);
	}

	graphCardHandle->SetTransform(matrixModes[matrixMode], &tempMat);


}

void MegaEngine::Renderer::setMatrixMode(Matrix_Mode mode)
{
	matrixMode = mode;
}

void MegaEngine::Renderer::setViewportPosition()
{
	D3DXMATRIX mat;
	D3DXVECTOR3 lookPos;

	lookPos.x = viewerPos->x;
	lookPos.y = viewerPos->y;
	lookPos.z = 0.0f;

	D3DXMatrixLookAtLH(&mat, viewerPos, &lookPos, viewerUp);
	if(graphCardHandle->SetTransform(D3DTS_VIEW, &mat) == D3D_OK)
	{
		*viewerMatrix = mat;
	}
	else
	{
		//MessageBox(*winHandle,"viewport fuck","fuck",MB_OK);
	}
}

void MegaEngine::Renderer::setViewportPosition(Camera* camera)
{
	D3DXMATRIX mat;
	D3DXVECTOR3 lookPos;
	D3DXVECTOR3 up; up.x = 0; up.y = 1; up.z = 0;

	lookPos.x = camera->viewerPos->x + camera->viewerForward->x;
	lookPos.y = camera->viewerPos->y + camera->viewerForward->y;
	lookPos.z = camera->viewerPos->z + camera->viewerForward->z;


	D3DXMatrixLookAtLH(&mat, camera->viewerPos, &lookPos, camera->viewerUp);//&up);//
	if(graphCardHandle->SetTransform(D3DTS_VIEW, &mat) == D3D_OK)
	{
		*viewerMatrix = mat;
	}
}

void MegaEngine::Renderer::Translate(float x, float y, float z)
{
	D3DXMATRIX tempMat;

	D3DXMatrixTranslation(&tempMat, x, y, z);

	graphCardHandle->MultiplyTransform(matrixModes[matrixMode], &tempMat);
}

void MegaEngine::Renderer::Scale(float x, float y, float z)
{
	D3DXMATRIX tempMat;

	D3DXMatrixScaling(&tempMat, x, y, z);

	graphCardHandle->MultiplyTransform(matrixModes[matrixMode], &tempMat);
}

void MegaEngine::Renderer::RotateX(float angle)
{
	D3DXMATRIX tempMat;

	D3DXMatrixRotationX(&tempMat, angle);

	graphCardHandle->MultiplyTransform(matrixModes[matrixMode], &tempMat);
}

void MegaEngine::Renderer::RotateY(float angle)
{
	D3DXMATRIX tempMat;

	D3DXMatrixRotationY(&tempMat, angle);

	graphCardHandle->MultiplyTransform(matrixModes[matrixMode], &tempMat);
}
void MegaEngine::Renderer::RotateZ(float angle)
{
	D3DXMATRIX tempMat;

	D3DXMatrixRotationZ(&tempMat, angle);

	graphCardHandle->MultiplyTransform(matrixModes[matrixMode], &tempMat);
}

D3DXMATRIX* MegaEngine::Renderer::getTransform()
{
	D3DXMATRIX* resul;
	graphCardHandle->GetTransform(matrixModes[matrixMode], resul);
	return resul;
}